﻿using System;
using Myotragus.Data.Metadata;
using Myotragus.Data.Util;

namespace Myotragus.Data.Hbm
{
	public abstract class HbmGenerator : IHbmGenerator
	{
		#region IoC Dependencies

		protected HbmGenerator(IEntityMapping mapping)
		{
			Mapping = mapping;
		}

		protected IEntityMapping Mapping { get; private set; }

		#endregion

		#region Consts

		/// <summary>
		/// HBM File format string.
		/// </summary>
		/// <remarks>
		/// Parameters:
		/// 0 - Assembly Name
		/// 1 - Namespace
		/// 2 - Class Name
		/// 3 - Table Name
		/// 4 - Properties
		/// </remarks>
		protected const string StrHbmFormat = @"<?xml version=""1.0"" encoding=""utf-8""?>

<hibernate-mapping xmlns=""urn:nhibernate-mapping-2.2"" assembly=""{0}""
				namespace=""{1}"">

	<class name=""{2}"" table=""{3}"">

	{4}

	</class>

</hibernate-mapping>";

		/// <summary>
		/// Format primary key
		/// </summary>
		protected const string StrIdFormat = @"
		<id name=""{0}"" column=""{1}"">
		<generator class=""{2}""/>
		</id>
";
		#endregion

		public virtual string GenerateMapping(Type interfaceType)
		{
			var persistentType = ResolveRersistentType(interfaceType);

			return string.Format(StrHbmFormat, persistentType.Assembly, persistentType.Namespace,
				persistentType.Name, Mapping.GetEntityTable(interfaceType),
				GenerateMappings(interfaceType));
		}

		protected virtual string GenerateMappings(Type interfaceType)
		{
			return string.Concat(GenerateIdMapping(interfaceType),
				GenerateNonIdFieldsMappings(interfaceType));
		}

		protected virtual string GenerateIdMapping(Type interfaceType)
		{
			var id = Mapping.GetPrimaryKey(interfaceType);

			if (id == null)
				throw ExecutionHelper.CreateException<ArgumentException>(
					Messages.EntityWithNoPk,
					interfaceType);

			return string.Format(StrIdFormat, id.PropertyName, 
				id.ColumnName, id.Generator);
		}

		protected abstract string GenerateNonIdFieldsMappings(Type interfaceType);

		protected abstract Type ResolveRersistentType(Type interfaceType);
	}
}